/*
 * Copyright (c) 2011 Imre Fazekas.
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the name of the Brillien nor the names of its
 * terms and concepts may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.vii.brillien.core.management.io;

import com.vii.brillien.core.management.BrillienServices;
import com.vii.brillien.core.management.component.MetaServices;
import com.vii.brillien.ignition.BrillienStatics;
import com.vii.brillien.kernel.BrillienException;
import com.vii.brillien.kernel.axiom.atomic.Presence;

import java.io.*;
import java.util.LinkedList;
import java.util.List;

/**
 * Service class helping IO-related operations extending the IOServices class defined in package: com.vii.streamline.services
 */
public class IOServices {

    /**
     * Allows to interface with the environment in which the application is running from the global res directory
     * @param cmdarray array containing the command to call and its arguments.
     * @return Process reference to the given execution
     * @throws BrillienException
     */
    public static Process exec( String[] cmdarray ) throws BrillienException {
        File f = new File( BrillienStatics.LIB_DIR + File.separator );

        try {
            return Runtime.getRuntime().exec(
                    cmdarray, null, f
            );
        } catch (Exception e) {
            throw new BrillienException( e.getMessage(), e );
        }
    }


    /**
     * Allows to interface with the environment in which the application is running from the local res directory of the given Presence
     * @param s caller Presence
     * @param cmdarray array containing the command to call and its arguments.
     * @return Process reference to the given execution 
     * @throws BrillienException
     */
    public static Process exec( Presence s, String[] cmdarray ) throws BrillienException {
        File f = new File( MetaServices.getJarReferenceName(
            BrillienServices.liaison.getPresenceManagerOf( s.getName() )
        ) + File.separator + BrillienStatics.LIB_DIR + File.separator );

        if( !f.exists() )
            throw new BrillienException("No library directory exists for the given Presence:" + s.getName() );

        try {
            return Runtime.getRuntime().exec(
                    cmdarray, null, f
            );
        } catch (Exception e) {
            throw new BrillienException( e.getMessage(), e );
        }
    }

    public static String getResourcePath( Presence s, String path ) throws FileNotFoundException {
        // 1. local library
        File f = new File( MetaServices.getJarReferenceName(
            BrillienServices.liaison.getPresenceManagerOf( s.getName() )
        ) + File.separator + BrillienStatics.RES_DIR + File.separator + path );
        if( f.exists() )
            return f.getAbsolutePath();

        // 2. local classpath
        f = new File( MetaServices.getJarReferenceName(
            BrillienServices.liaison.getPresenceManagerOf( s.getName() )
        ) + File.separator + BrillienStatics.BIN_DIR + File.separator + path );
        if( f.exists() )
            return f.getAbsolutePath();

        // 3. global library
        f = new File( BrillienStatics.RES_DIR + File.separator + path );
        if( f.exists() )
            return f.getAbsolutePath();

        // 4. global root folder
        f = new File( path );
        if( f.exists() )
            return f.getAbsolutePath();

        throw new FileNotFoundException( "No resource found..." );
    }


    /**
     * Gets Resource of a Presence by its name. All presence instance has a resource directory which is shared among components
     * @param presenceName Presence referenceName
     * @param path Path of the resource relative to the Presence's folder
     * @return Inputstream to the resource found
     * @throws FileNotFoundException in no resource found
     */
    public static InputStream getResource( String presenceName, String path ) throws FileNotFoundException {
        // 1. local library
        File f = new File( BrillienStatics.ACTIVATOR_DIR + File.separator + MetaServices.getJarReferenceName(
                BrillienServices.liaison.getPresenceManagerOf( presenceName )
        ) + File.separator + BrillienStatics.RES_DIR + File.separator + path );
        if( f.exists() )
            return new FileInputStream( f );

        // 2. local classpath
        f = new File( BrillienStatics.ACTIVATOR_DIR + File.separator + MetaServices.getJarReferenceName(
                BrillienServices.liaison.getPresenceManagerOf( presenceName )
        ) + File.separator + BrillienStatics.BIN_DIR + File.separator + path );
        if( f.exists() )
            return new FileInputStream( f );

        // 3. global library
        f = new File( BrillienStatics.RES_DIR + File.separator + path );
        if( f.exists() )
            return new FileInputStream( f );

        // 4. global root folder
        f = new File( path );
        if( f.exists() )
            return new FileInputStream( f );

        throw new FileNotFoundException( "No resource found..." );
    }
    
    /**
     * Gets Resource of a Presence. All presence instance has a resource directory which is shared among components
     * @param s Presence reference
     * @param path Path of the resource relative to the Presence's folder
     * @return Inputstream to the resource found
     * @throws FileNotFoundException in no resource found
     */
    public static InputStream getResource( Presence s, String path ) throws FileNotFoundException {
        return getResource( s.getName(), path );
    }

    /**
     * Serialize an array of objects into byte array
     * @param ss array of objects to be serialized
     * @return serialized byte array
     * @throws IOException
     */
    public static <T extends Serializable> byte[] serializeAll( T... ss ) throws IOException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream(  );
        ObjectOutputStream out = new ObjectOutputStream( bout );
        for( Serializable s : ss )
            out.writeObject( s );
        out.close();
        return bout.toByteArray();
    }

    /**
     * Deserialize a byte array
     * @param b byte array to be deserialized
     * @param <T> Generic type of the serialized object
     * @return array of deserialized objects
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static <T extends Serializable> List<T> deSerializeAll( byte[] b ) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bin = new ByteArrayInputStream( b );
        ObjectInputStream in = new ObjectInputStream( bin );

        LinkedList<T> list = new LinkedList<T>();

        try{
            while( true ){
                T t = (T)in.readObject();
                list.add( t );
            }
        } catch( Exception e ){ }

        in.close();

        return list;
    }

/**
     * Converts a class name into path name
     * @param name class name
     * @return derived path name
     */
    public static String getClassNameAsPath( String name ){
        return name.replaceAll("\\.", File.separator);
    }

    /**
     * Converts a package name into path name
     * @param name package name
     * @return derived path name
     */
    public static String getPackageNameAsPath( String name ){
        return getClassNameAsPath( name.substring(0, name.lastIndexOf('.')) );
    }

    /**
     * Delete a file
     */
    public static boolean delete( File f ){
        return f.delete();
    }

    /**
     * String conversion replacing a problematic character with a convenient one.
     * @param name string to be conformized
     * @return conformized string
     */
    public static String conformToStorage( String name ){
        return name.replaceAll("/", "_").replaceAll(":", "-");
    }

    /**
     * Creates a path including all necessary directories.
     * @param path path to be created
     * @return String representation of the path
     */
    public static String createDirectory( String... path ){
        StringBuilder sb = new StringBuilder();
        for( String p : path )
            sb.append( p + File.separator );
        new File( sb.toString() ).mkdirs();
        return sb.toString();
    }


}

